package com.luxf.base.domainevent.utils;

import com.luxf.base.domainevent.EventNoticeManager;
import com.luxf.base.domainevent.annotation.AggregateRootId;
import com.luxf.base.domainevent.annotation.EventVersion;
import com.luxf.base.domainevent.codec.MessageEncoder;
import com.luxf.base.domainevent.configuration.DomainEventEnvironment;
import com.luxf.base.domainevent.constants.EventNoticeConstants;
import com.luxf.base.domainevent.context.LocalUserHolder;
import com.luxf.base.domainevent.model.DomainEventNotice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/**
 * 领域事件工具类
 *
 * @author fast66
 * 2021-05-22 8:36
 **/
@Slf4j
public final class DomainEventUtils {

    private static String applicationName;

    @Resource
    private MessageEncoder messageEncoder;

    @Resource
    private EventNoticeManager eventNoticeManager;

    @Resource
    private DomainEventEnvironment domainEventEnvironment;

    private static DomainEventUtils instance;

    @PostConstruct
    public void init() {
        instance = this;
    }

    @Value("${spring.application.name:default}")
    public void setApplicationName(String applicationName) {
        DomainEventUtils.applicationName = applicationName;
    }


    public static void apply(DomainEventSupportable aggregate, Object event) {
        apply(aggregate, event, false);
    }


    public static void apply(DomainEventSupportable aggregate, Object event, boolean isRaise) {
        if (Objects.isNull(event)) {
            log.debug("接收到event为null值！");
            return;
        }
        Assert.notNull(aggregate, "聚合根不能为null！");
        if (isRaise && instance.domainEventEnvironment.isRocketMQEnabled()) {
            DomainEventNotice notice = raise(aggregate, event);
            aggregate.addEvent(notice);
        } else {
            aggregate.addEvent(event);
        }
        // 发布事件.
        aggregate.publishEvents();
    }

    /**
     * 将普通领域事件升级为领域事件通知
     *
     * @param event event
     * @return DomainEventNotice
     */
    private static DomainEventNotice raise(DomainEventSupportable aggregate, Object event) {
        return raise(aggregate, event, new HashMap<>());
    }

    /**
     * 将普通领域事件升级为领域事件通知
     *
     * @param aggregate
     * @param event
     * @param customerHeader
     * @return
     */
    private static DomainEventNotice raise(DomainEventSupportable aggregate, Object event,
                                           Map<String, String> customerHeader) {
        Map<String, String> header = new HashMap<>(customerHeader);
        LocalUserHolder.getLocalUser().ifPresent(user ->
                header.put(EventNoticeConstants.HEADER_LOCAL_USER, instance.messageEncoder.encode(user)));

        // 来源
        header.put(EventNoticeConstants.SOURCE_SERVICE, applicationName);
        // 设置消息id
        header.put(EventNoticeConstants.MESSAGE_ID, UUID.randomUUID().toString());
        // 记录类型
        header.put(EventNoticeConstants.CLASS_TYPE, event.getClass().getName());
        // 记录版本号
        header.put(EventNoticeConstants.CLASS_VERSION, getEventVersion(event.getClass()));
        // 记录聚合根id
        String aggregateId = getAggregateId(aggregate);
        header.put(EventNoticeConstants.AGGREGATE_ID, aggregateId);
        // 序列化
        DomainEventNotice notice =
                DomainEventNotice.builder().header(header).payload(instance.messageEncoder.encode(event)).build();

        instance.eventNoticeManager.markNone(notice);

        return notice;
    }

    private static String getAggregateId(DomainEventSupportable aggregate) {
        String[] id = {""};
        ReflectionUtils.doWithFields(aggregate.getClass(), field -> {
            ReflectionUtils.makeAccessible(field);
            id[0] = Objects.toString(field.get(aggregate));
        }, field -> field.isAnnotationPresent(AggregateRootId.class));
        return id[0];
    }

    /**
     * 获取事件版本号
     *
     * @param target clazz
     * @return version
     */
    public static String getEventVersion(Class<?> target) {
        EventVersion eventVersion = target.getAnnotation(EventVersion.class);
        if (Objects.nonNull(eventVersion)) {
            return eventVersion.value();
        } else {
            return EventVersion.DEFAULT_VERSION;
        }
    }
}